package io.gitee.cofenghen.jdbctempleplus;

import io.gitee.cofenghen.jdbctempleplus.RowMapper.CustomBeanPropertyRowMapper;
import io.gitee.cofenghen.jdbctempleplus.model.PageResult;
import io.gitee.cofenghen.jdbctempleplus.model.SelectPageMapListSqlModel;
import io.gitee.cofenghen.jdbctempleplus.support.dialect.DefaultSQLDialect;
import io.gitee.cofenghen.jdbctempleplus.support.dialect.DialectStrategy;
import jakarta.annotation.Resource;
import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * @author xxr
 * @since 2023-08-04 14:23
 */

public class BaseJdbcTemplePlus extends  AbstratcJdbcTemplePlusOperations{
    protected final Log logger = LogFactory.getLog(this.getClass());
    private static final Map<String, Supplier<DialectStrategy>> DIALECT_REGISTRY = new ConcurrentHashMap<>();
    // 静态初始化块，注册内置方言
    static {


        registerDialect("Default",  DefaultSQLDialect::new);
    }
    /**
     * 注册新的数据库方言
     * @param databaseProductName 数据库产品名称（如 MySQL、Oracle 等）
     * @param dialectSupplier 方言实例的供应函数
     */
    public static void registerDialect(String databaseProductName, Supplier<DialectStrategy> dialectSupplier) {
        DIALECT_REGISTRY.put(databaseProductName.toLowerCase(), dialectSupplier);
    }



    /**
     * 检测数据库类型并返回相应的方言
     * @param dataSource 数据源
     * @return 匹配的方言实现，如果未找到则返回默认方言
     */
    @SneakyThrows
    private DialectStrategy detectDialect(DataSource dataSource) {



        try (Connection connection = dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            String productName = metaData.getDatabaseProductName().toLowerCase();

         var  dialect=   DIALECT_REGISTRY.get(productName);
           if (dialect!=null)
           {
             return   dialect.get();

           }

           //读取不到 ，读取默认的方言
           var _default =   DIALECT_REGISTRY.get("Default".toLowerCase());
            if (_default!=null)
                return _default.get();

            // 如果未找到匹配的方言，抛出异常或返回默认实现
            throw new UnsupportedOperationException("不支持的数据库类型: " + productName);

        } catch (SQLException e) {
            throw new RuntimeException("检测数据库方言时出错", e);
        }
    }


    @Resource
    @Getter
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;


    public int update(String nameSql, Object bean) throws DataAccessException {
        SqlParameterSource sqlParameterSource = super.convertor(bean);
        return this.getNamedParameterJdbcTemplate().update(nameSql, sqlParameterSource);
    }
    @Override
    public int update(String nameSql, Map<String, ?> paramMap) throws DataAccessException {
        SqlParameterSource sqlParameterSource = new MapSqlParameterSource(paramMap);
        return this.getNamedParameterJdbcTemplate().update(nameSql, sqlParameterSource);
    }

    @Override
    public int commonUpdate(String sql, Object... args) throws DataAccessException {
      //  return this.getJdbcTemplate().update(sql, args);
        return this.getNamedParameterJdbcTemplate().getJdbcOperations().update(sql, args);
    }


    /**
     * 获取第一行第一列
     * @param nameSql      命名语法SQL;例如:SELECT name FROM users WHERE name = :userName, age = :age
     * @param paramMap     Map类型的key，替换命名参数
     * @param requiredType 返回的Bean类型
     * @return 返回泛型
     */
    @SneakyThrows
    @Override
    public <T> T selectColumn00(String nameSql, Map<String, ?> paramMap, Class<T> requiredType) throws DataAccessException {
        SqlParameterSource sqlParameterSource = super.convertor(paramMap);
        try {


          return this.getNamedParameterJdbcTemplate().queryForObject(nameSql, sqlParameterSource, requiredType);


        } catch (EmptyResultDataAccessException ex) {
            return null;
        }

    }


    /**
     * 获取第一行 ，存在多笔结果抛出异常
     * @param nameSql sql
     * @param paramMap 参数
     * @param requiredType 返回的的泛型类
     * @return 返回泛型
     */
    @SneakyThrows
    @Override
    public <T> T selectOne(String nameSql, Map<String, ?> paramMap, Class<T> requiredType) throws DataAccessException {
        SqlParameterSource sqlParameterSource = super.convertor(paramMap);
        if (requiredType.getClass().getName().equals("java.lang.Class")) {
            recodelogPreparing(nameSql,paramMap);
          //  RowMapper<T> rowMapper = new BeanPropertyRowMapper<T>(requiredType);
            RowMapper<T> rowMapper = new CustomBeanPropertyRowMapper<>(requiredType);
            var list = this.getNamedParameterJdbcTemplate().query(nameSql, sqlParameterSource, rowMapper);
            if (list.size()>1) {
                throw new IncorrectResultSizeDataAccessException(1, list.size());
            }
            if (list.size()>0)
            {
                return  list.get(0);
            }
        }
        else
        {
            throw  new RuntimeException("该方法selectOne只允许传递Java.Lang.Class!");
        }

        return  null;

    }

    private   void  recodelogPreparing(String  sql ,Map<String,?> paramMap)
    {

       if (logger.isDebugEnabled())
       {
           logger.debug("=>Preparing: "+sql);

           StringBuilder stringBuilder=new StringBuilder();
           if (paramMap!=null) {
               for (Map.Entry<String, ?> entry : paramMap.entrySet()) {
                   stringBuilder.append( ","+entry.getKey() +":"+ entry.getValue());
               }
           }
           logger.debug("=>Parameters: "+stringBuilder.substring(1));
       }


    }

    @SneakyThrows
    @Override
    public Map<String, Object>   selectOne(String nameSql, Map<String, ?> paramMap) throws DataAccessException {

              var list =  this.select(nameSql, paramMap);

        if (list.size()>1) {
            throw new IncorrectResultSizeDataAccessException(1, list.size());
        }
        if (list.size()>0)
        {
            return  list.get(0);
        }

            return  null;
     }



    @Override
    public <T> T selectObject(String nameSql, Object bean, Class<T> requiredType) throws DataAccessException {
        SqlParameterSource sqlParameterSource = super.convertor(bean);
        RowMapper<T> rowMapper = new BeanPropertyRowMapper<>(requiredType);
        var list = this.getNamedParameterJdbcTemplate().query(nameSql, sqlParameterSource, rowMapper);
        if (list.size()>0)
        {
            return  list.get(0);
        }

        return  null;
    }

    @Override
    public Map<String, Object> selectMap(String nameSql, Map<String, ?> paramMap) throws DataAccessException {
        SqlParameterSource sqlParameterSource = super.convertor(paramMap);
        return this.getNamedParameterJdbcTemplate().queryForMap(nameSql, sqlParameterSource);
    }

    @Override
    public Map<String, Object> selectMap(String nameSql, Object bean) throws DataAccessException {
        SqlParameterSource sqlParameterSource = super.convertor(bean);
        return this.getNamedParameterJdbcTemplate().queryForMap(nameSql, sqlParameterSource);
    }

    @Override
    public List<Map<String, Object>> select(String nameSql, Map<String, ?> paramMap) throws DataAccessException {
        return this.getNamedParameterJdbcTemplate().queryForList(nameSql, paramMap);
    }

    @Override
    public List<Map<String, Object>> select(String nameSql, Object bean) throws DataAccessException {
        SqlParameterSource sqlParameterSource = super.convertor(bean);
        return this.getNamedParameterJdbcTemplate().queryForList(nameSql, sqlParameterSource);
    }

    @Override
    public <T> List<T> select(String nameSql, Class<T> mappedClass) throws DataAccessException {
        RowMapper<T> rowMapper = new BeanPropertyRowMapper<>(mappedClass);
        return this.getNamedParameterJdbcTemplate().query(nameSql, rowMapper);
    }
    @Override
    public    <T> List<T> select(String nameSql, Map<String, ?> paramMap,Class<T> mappedClass) throws DataAccessException
    {
        RowMapper<T> rowMapper = new BeanPropertyRowMapper<>(mappedClass);
        return this.getNamedParameterJdbcTemplate().query(nameSql,paramMap ,rowMapper);
    }
    @SneakyThrows
    @Override
    public  <T> PageResult<T> selectPageList(String sql_table, String sql_column, String sql_where, Map<String,Object> paramMap, String pk_Filed, String orderField, String orderType, LinkedHashMap<String,String> defaultOrderFieldMap, int pageIndex, int pageSize, String sql_column_top, Class<T> mappedClass)
    {
       return   selectPageList(  sql_table,   sql_column,   sql_where,   paramMap,   pk_Filed,   orderField,   orderType,   defaultOrderFieldMap,   pageIndex,   pageSize,   sql_column_top,mappedClass,true  );
    }
    @SneakyThrows
    @Override
    public <T> PageResult<T> selectPageList(String sql_table, String sql_column, String sql_where, Map<String, Object> paramMap, String pk_Filed, String orderField, String orderType, LinkedHashMap<String, String> defaultOrderFieldMap, int pageIndex, int pageSize, String sql_column_top, Class<T> mappedClass,boolean selectCount) throws DataAccessException {

        if (null==defaultOrderFieldMap)
            defaultOrderFieldMap=new LinkedHashMap<>();

        if (null==paramMap)
            paramMap=new LinkedHashMap<>();

        PageResult<T> result = new PageResult<>();
        if (pageIndex == 0)
            pageIndex = 1;
        if (pageSize == 0)
            pageSize = 10; //默认10笔数据


        var  sqlData =   selectPageMapListSql(  sql_table,   sql_column,   sql_where,   paramMap,   pk_Filed,   orderField,   orderType,  defaultOrderFieldMap,   pageIndex,   pageSize,   sql_column_top,  selectCount);

        if (sqlData.getCountSql()!=null)
        {
            var _scalarResult = this.selectColumn00(sqlData.getCountSql(), paramMap, Long.class);
            result.setTotalRecordCount(_scalarResult);
        }
        var pageList = this.select(sqlData.getDetailSql(), paramMap,mappedClass);


        result.setList(pageList);
        return result;
    }

    @SneakyThrows
    @Override
    public   PageResult<Map<String, Object>> selectPageMapList(String sql_table, String sql_column, String sql_where, Map<String, Object> paramMap, String pk_Filed, String orderField, String orderType, LinkedHashMap<String, String> defaultOrderFieldMap, int pageIndex, int pageSize, String sql_column_top) throws DataAccessException {

        return  selectPageMapList(  sql_table,   sql_column,   sql_where,   paramMap,   pk_Filed,   orderField,   orderType,  defaultOrderFieldMap,   pageIndex,   pageSize,   sql_column_top,  true);
    }


    public SelectPageMapListSqlModel selectPageMapListSql(String sql_table, String sql_column, String sql_where, Map<String, Object> paramMap, String pk_Filed, String orderField, String orderType, LinkedHashMap<String, String> defaultOrderFieldMap, int pageIndex, int pageSize, String sql_column_top, boolean selectCount)
    {


         var dialect = detectDialect(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource());
         return dialect.getLimitSql(  sql_table,   sql_column,   sql_where,  pk_Filed,  orderField,  orderType,  defaultOrderFieldMap,  pageIndex,  pageSize,  sql_column_top,  selectCount);


    }
    @SneakyThrows
    @Override
    public   PageResult<Map<String, Object>> selectPageMapList(String sql_table, String sql_column, String sql_where, Map<String, Object> paramMap, String pk_Filed, String orderField, String orderType, LinkedHashMap<String, String> defaultOrderFieldMap, int pageIndex, int pageSize, String sql_column_top,boolean selectCount) throws DataAccessException {


        if (null==defaultOrderFieldMap)
            defaultOrderFieldMap=new LinkedHashMap<>();

        if (null==paramMap)
            paramMap=new LinkedHashMap<>();


        PageResult<Map<String, Object>> result = new PageResult<>();
        SelectPageMapListSqlModel sqlData =   selectPageMapListSql(  sql_table,   sql_column,   sql_where,   paramMap,   pk_Filed,   orderField,   orderType,  defaultOrderFieldMap,   pageIndex,   pageSize,   sql_column_top,  selectCount);

        if (sqlData.getCountSql()!=null)
        {
            var _scalarResult = this.selectColumn00(sqlData.getCountSql(), paramMap, Long.class);
            result.setTotalRecordCount(_scalarResult);
        }
        var pageList = this.select(sqlData.getDetailSql(), paramMap);
        result.setList(pageList);
        return result;

    }



    /**
     * 公共的查出第1列
     * @param sql          占位符语法SQL;例如:SELECT * FROM users WHERE name = ?, age = ?
     * @param requiredType 返回的Bean类型
     * @param args         数组类型，替换占位符参数
     * @return 返回泛型
     */
    @Override
    public <T> T commonSelectCloumn00(String sql, Class<T> requiredType, Object... args) throws DataAccessException {

           return this.getNamedParameterJdbcTemplate().getJdbcOperations().queryForObject(sql, requiredType, args);
    }

    @Override
    public Map<String, Object> commonSelectMap(String sql, Object... args) throws DataAccessException {
      ///  return this.getJdbcTemplate().queryForMap(sql, args);
        return this.getNamedParameterJdbcTemplate().getJdbcOperations().queryForMap(sql, args);
    }

    /**
     * 这是一个过时的方法。
     *
     * @deprecated 此方法已过时，请使用 {@link #select(String, Map)} (String, Object)} ()} 代替。
     */
    @Deprecated
    @Override
    public List<Map<String, Object>> commonSelect(String sql, Object... args) throws DataAccessException {
       // return this.getJdbcTemplate().queryForList(sql, args);
        return  this.getNamedParameterJdbcTemplate().getJdbcOperations().queryForList(sql, args);
    }

    @Override
    public <T> List<T> commonSelect(String sql, Class<T> mappedClass) throws DataAccessException {
        RowMapper<T> rowMapper = this.convertor(mappedClass);
      //  return this.getJdbcTemplate().query(sql, rowMapper);
        return this.getNamedParameterJdbcTemplate().getJdbcOperations().query(sql, rowMapper);
    }

    @Override
    public <T> List<T> commonSelect(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
       // return this.getJdbcTemplate().query(sql, rowMapper, args);
        return  this.getNamedParameterJdbcTemplate().getJdbcOperations().query(sql, rowMapper, args);
    }

    public <T> T selectObject(String sql, Class<T> requiredType, Object... args) throws DataAccessException {
        //return this.getJdbcTemplate().queryForObject(sql, requiredType, args);
        return   this.getNamedParameterJdbcTemplate().getJdbcOperations().queryForObject(sql, requiredType, args);
    }

}
